﻿using System;
using System.Text.RegularExpressions;

namespace Adjunct.Framework
{
	public class SemanticVersion : IComparable<SemanticVersion>
	{
		public const string Pattern = @"\d+(\.\d+){1,2}((\.\d+)?|(-[0-9a-zA-Z-]+(~\d+)?)?)";
		public const string PatternLine = "^" + Pattern + "$";
		private static readonly Regex LineRegex = new Regex(PatternLine);

		public SemanticVersion(string semVer)
		{
			if (!LineRegex.IsMatch(semVer))
			{
				throw new Exception("Not a valid SemVer: " + semVer);
			}
			Value = semVer;
			Build = int.MaxValue;
			PreReleaseTag = "";
			int dash = semVer.IndexOf('-');
			if (dash == -1)
			{
				Version = NormalizeVersion(semVer);
				return;
			}
			Version = NormalizeVersion(semVer.Substring(0, dash));
			dash++;
			int tilda = semVer.IndexOf('~');
			if (tilda == -1)
			{
				PreReleaseTag = semVer.Substring(dash);
				return;
			}
			PreReleaseTag = semVer.Substring(dash, tilda - dash);
			Build = int.Parse(semVer.Substring(tilda + 1));
		}

		public int Build { get; private set; }
		public string PreReleaseTag { get; private set; }
		public string Value { get; private set; }
		public Version Version { get; private set; }

		public int CompareTo(SemanticVersion other)
		{
			if (other == null)
			{
				return 1;
			}
			//version
			int result = Version.CompareTo(other.Version);
			if (result != 0)
			{
				return result;
			}
			//no tag > pre tag; pre tags in lexicographic
			if (PreReleaseTag.Length == 0 && other.PreReleaseTag.Length > 0)
			{
				return 1;
			}
			if (PreReleaseTag.Length > 0 && other.PreReleaseTag.Length == 0)
			{
				return -1;
			}
			result = String.CompareOrdinal(PreReleaseTag, other.PreReleaseTag);
			if (result != 0)
			{
				return result;
			}
			//build
			return Build.CompareTo(other.Build);
		}

		private static Version NormalizeVersion(string value)
		{
			Version version = Version.Parse(value);
			return new Version(version.Major, version.Minor, Math.Max(version.Build, 0), Math.Max(version.Revision, 0));
		}
	}
}